// OGoMojo
// Whitemice.MOGI.ZOGI.zOGI
// License: LGPL
// Copyright: Whitemice Consulting, 2006
// Author(s): Adam Tauno Williams (awilliam@whitemice.org)
using System;
using System.Net;
using System.Collections;
using CookComputing.XmlRpc;
using Whitemice.MOGI;

namespace Whitemice.MOGI.ZOGI
{
	public class zOGI
	{
		private 	zOGIProxy            				proxy;
    protected XmlRpcClientProtocol  			client;
    protected Whitemice.MOGI.ZOGI.Server	server;
    protected NetworkCredential     			credentials;
    protected	string											hostname;
    protected	Cache												cache;
    
		public zOGI(Whitemice.MOGI.ZOGI.Server server)
		{
			this.server = server;
			cache = new Cache();
			proxy = (zOGIProxy)XmlRpcProxyGen.Create(typeof(zOGIProxy));
			client = (XmlRpcClientProtocol)proxy;
			client.ProtocolVersion = HttpVersion.Version10;
			client.PreAuthenticate = true;
			client.Timeout = 1800000;
			Credentials = new NetworkCredential("awilliam", "fred");
			Hostname = "localhost";
		}
		
		internal bool Connected
		{
			get 
			{
				bool 		connected = true;
				object	result;
				try { result = proxy.GetLoginAccount(0); }
					catch (Exception ex)
					{
						Console.WriteLine("zOGI::Connected Failed with {0}", ex.Message);
						connected = false;
						result = null;
					}
				Console.WriteLine("Result type is {0}", result.GetType().Name);
				if (connected)
					server.SetCurrentActor((IAccount)this.FactoryFromStruct((XmlRpcStruct)result));				
				return connected;
			}
		}
		
 		internal NetworkCredential Credentials
 		{
 			get { return credentials; }
 			set
 			{
   			credentials = value;
  			client.Credentials = credentials;
  			Hostname = Hostname;
 			}
 		}

		public string Hostname
		{
			get { return hostname; }
			set
			{
				hostname = value;
				client.Url = "http://" + hostname + "/zidestore/so/" + Credentials.UserName + "/";
			}
		}
		
		public IMOGIObject GetObjectByName(string documentName)
		{
			return GetObjectByName(documentName, true);
		}
		
  	public IMOGIObject GetObjectByName(string documentName, bool useCache)
  	{
  		switch(documentName.Split('/').Length)
  		{
  			case 3:
  				return GetObjectById(documentName.Split('/')[2], useCache);
  			case 4:
  				switch(documentName.Split('/')[0])
  				{
  					case "Annotation":
  						switch(MOGI.DetermineType(documentName))
  						{
  							case DocumentType.Task:
  								XmlRpcStruct xmlEntry;
  								xmlEntry = new XmlRpcStruct();
  								xmlEntry.Add("entityName", "taskNotation");
       						xmlEntry.Add("objectId", 0);
       						xmlEntry.Add("action", "comment");
       						xmlEntry.Add("actionDate", DateTime.Now);
       						xmlEntry.Add("taskObjectId", documentName.Split('/')[3]);
       						xmlEntry.Add("taskStatus", "");
       						xmlEntry.Add("actorId", 0);
       						xmlEntry.Add("comment", "");
									return FactoryFromStruct(xmlEntry);
  						}
  						break;
  				}
  				break;
  		}
  		return null;
  	}
  	
  	public IMOGIObject GetObjectById(string id)
  	{
  		return GetObjectById(id, true);
  	}
  	
  	public void PutObject(IMOGIObject obj)
  	{
  		XmlRpcStruct	oldHash;
  		XmlRpcStruct	newHash;
  		int						id;
  		
  		if(obj is Whitemice.MOGI.ZOGI.Document)
  		{
  			newHash = new XmlRpcStruct();
  			oldHash = (XmlRpcStruct)((Whitemice.MOGI.ZOGI.Document)obj).Payload.Hash.Clone();
  			foreach(string key in oldHash.Keys)
  				if(key.Substring(1,2) == "_")
  				{
  				} else
  					{
  						newHash.Add(key, oldHash[key]);
  					}
  			id = proxy.PutObject(newHash);
  			if (id > 0)
  				((Whitemice.MOGI.ZOGI.Document)obj).Payload = 
  					new Payload(proxy.GetObject(id, 65535));
  		}
  	}
  	
  	public IMOGIObject GetObjectById(string id, bool useCache)
  	{
  		int						objectId;
  		XmlRpcStruct	hash;
  		IMOGIObject		obj;
  		
  		if(useCache)
  			if(cache.Contains(id))
  			{
  				Console.WriteLine("Object {0} retrieved from cache", id);
  				return cache.Get(id);
  			}
  		else Console.WriteLine("Processing uncached request");
  		Console.WriteLine("Getting object {0} from server", id);
  		objectId = Convert.ToInt32(id);
  		try { hash = proxy.GetObject(objectId, 65535); }
  			catch (Exception ex)
  			{
  				Console.WriteLine("Exception: {0}", ex.Message);
  				hash = null;
  			}
  		if (hash == null) return null;
  		obj = FactoryFromStruct(hash);
  		if (obj != null)
  			cache.Put(obj);
  		return obj;
  	}
  	
  	public ArrayList GetFavorites(DocumentType documentType)
  	{
  		switch(documentType)
  		{
 				case DocumentType.Contact:
 					return FactoryFromStructs(proxy.GetFavoritesByType("Contact", 65535));
 				case DocumentType.Project:
 					return FactoryFromStructs(proxy.GetFavoritesByType("Project", 65535));
 				case DocumentType.Enterprise:
 					return FactoryFromStructs(proxy.GetFavoritesByType("Enterprise", 65535)); 					
 			}
 			return null;
  	}
  	
  	public ArrayList GetTaskAnnotations(string name)
  	{
  		Task task;
  		
  		task = (Task)GetObjectByName(name);
  		return FactoryFromStructs(task.Payload.GetStructsForKey("_NOTES"));
  	}
  	
  	public IRelation GetProjectForTask(string name)
  	{
  		string projectId;
  		
  		projectId = ((Task)GetObjectByName(name)).Payload.GetStringForKey("objectProjectId");
  		if (projectId.Length > 0)
  			return new Relation(ERelationType.MemberOf, name,
  															"Document/Project/" + projectId,
  															"", "", "");
  		return null;
  	}
  	
  	public ArrayList GetDocumentAudit(string name)
  	{
  		Document document;
  		
  		document = (Document)GetObjectByName(name);
  		return FactoryFromStructs(document.Payload.GetStructsForKey("_LOGS"));
  	}
  	
  	public ArrayList GetObjectLinks(string name)
  	{
  		Document	document;
  		ArrayList	links;
  		
  		document = (Document)GetObjectByName(name);
  		links = new ArrayList();
  		foreach(XmlRpcStruct xmlStruct in document.Payload.GetStructsForKey("_OBJECTLINKS"))
  			if ((string)xmlStruct["direction"] == "from")
  			{
  				links.Add(new Relation(ERelationType.ObjectLink,
  															name,
  															"Document/" +
  																(string)xmlStruct["targetEntityName"] + 
  																"/" +
  																(string)xmlStruct["targetObjectId"],
  															(string)xmlStruct["label"],
  															"",
  															(string)xmlStruct["objectId"]
  														)
  													);
  			}	else
  				{
	  				links.Add(new Relation(ERelationType.ObjectLink,
	  															"Document/" +
	  																(string)xmlStruct["targetEntityName"] + 
	  																"/" +
	  																(string)xmlStruct["targetObjectId"],
	  															name,
	  															(string)xmlStruct["label"],
	  															"",
	  															(string)xmlStruct["objectId"]
	  														)
	  													);  				
  				}
  		return links;
  	}
  	
		protected ArrayList FactoryFromStructs(Array hashes)
  	{
  		ArrayList		results;
  
  		results = new ArrayList();
  		foreach(object entry in hashes)
  		{
  			XmlRpcStruct xmlEntry = entry as XmlRpcStruct;
  			if (xmlEntry == null)
  				Console.WriteLine("FactoryFromStructs; object is a {0}", entry.GetType());
  			else
  				results.Add(FactoryFromStruct(xmlEntry));
  		}	
  		return results;
		}
		
		protected IMOGIObject FactoryFromStruct(XmlRpcStruct hash)
		{
			Payload 			payload;
			IMOGIObject		mogiObject;
				
			mogiObject = null;
			payload = new Payload(hash);
			Console.WriteLine("zOGI.FactoryFromStruct: {0}/{1}",
				payload.GetStringForKey("entityName"),
				payload.GetIntForKey("objectId").ToString());
			switch(payload.GetStringForKey("entityName"))
			{
				case "Account":
						mogiObject = new Whitemice.MOGI.ZOGI.Account(this.server, payload);
					break;
				case "Contact":
						mogiObject = new Whitemice.MOGI.ZOGI.Contact(this.server, payload);
					break;
				case "Enterprise":
						mogiObject = new Whitemice.MOGI.ZOGI.Enterprise(this.server, payload);
					break;
				case "Project":
						mogiObject = new Whitemice.MOGI.ZOGI.Project(this.server, payload);
					break;
				case "Task":
				    Console.WriteLine("zOGI.FactoryFromStruct: making Task {0}", 
				    	payload.GetIntForKey("objectId").ToString());
						mogiObject = new Whitemice.MOGI.ZOGI.Task(this.server, payload);						
					break;
				case "taskNotation":
						Console.WriteLine("zOGI.FactoryFromStruct: making TaskAnnotation {0}",
							payload.GetIntForKey("objectId").ToString());
						mogiObject = new Whitemice.MOGI.ZOGI.TaskAnnotation(this.server, payload);
					break;
				case "logEntry":
						Console.WriteLine("zOGI.FactoryFromStruct: making logEntry {0}",
							payload.GetIntForKey("objectId").ToString());
						mogiObject = new Whitemice.MOGI.ZOGI.AuditEntry(this.server, payload);
					break;					
				case "Team":
						mogiObject = new Whitemice.MOGI.ZOGI.Team(this.server, payload);
					break;
				default:
					Console.WriteLine("zOGI.FactoryFromStruct: Unknown Entity Name!!!"); 
					break;
			}
			return mogiObject;
		}
		
		public void AsyncGetFavorites(DocumentType documentType, AsyncSearchCallback callback, object context)
		{
			Hashtable			state;
			AsyncCallback zOGIcallback;
			
			server.Log.Debug("AsyncGetFavorites");
			state = new Hashtable();
			state.Add("callback", callback);
			state.Add("context", context);
			zOGIcallback = new AsyncCallback(MultiResultCallback);
			switch(documentType)
			{
				case DocumentType.Contact:
					server.Log.Debug("AsyncGetFavorites - Contacts");
					state.Add("method", "GetFavoriteContacts");
					proxy.BeginGetFavoritesByType("Contact", 65535, zOGIcallback, state);
					break;
				case DocumentType.Enterprise:
					state.Add("method", "GetFavoriteEnterprises");
					proxy.BeginGetFavoritesByType("Enterprise", 65535, zOGIcallback, state);
					break;
				case DocumentType.Project:
					state.Add("method", "GetFavoriteProjects");
					proxy.BeginGetFavoritesByType("Project", 65535, zOGIcallback, state);
					break;
			}
		}
		
		public void AsyncGetTaskList(string taskList, AsyncSearchCallback callback, object context)
		{
			Hashtable			state;
			AsyncCallback zOGIcallback;
			string 				method;
			
			Console.WriteLine("AsyncGetTaskList:taskList={0}", taskList);
			server.Log.Debug("AsyncGetTaskList");
			state = new Hashtable();
			state.Add("callback", callback);
			state.Add("context", context);
			zOGIcallback = new AsyncCallback(MultiResultCallback);
			switch(taskList)		
			{
				case "todo": method = "GetTaskList:todo"; break;
				case "delegated": method = "GetTaskList:delegated"; break;
				case "archived": method = "GetTaskList:archived"; break;
				default: method = null; break;
			}
			if (method != null)
			{
				Console.WriteLine("AsyncGetTaskList:method={0}", method);
				state.Add("method", method);
				proxy.BeginGetTasksByList(taskList, 65535, zOGIcallback, state);
			}	else Console.WriteLine("AsyncGetTaskList - null method");
		}
		
		public int PutTaskNotation(string id, string action, string comment)
		{
			//bool result;
			return proxy.PutTaskNotation(id, action, comment);
		}
		
		public void AsyncGetObject(int objectId, AsyncSearchCallback callback, object context)
		{
			Hashtable state;
			AsyncCallback zOGIcallback;
			
			state = new Hashtable();
			state.Add("callback", callback);
			state.Add("context", context);
			state.Add("method", "GetObject");
		  zOGIcallback = new AsyncCallback(SingleResultCallback);
		  proxy.BeginGetObject(objectId, 65535, zOGIcallback, state);
		}
		
		public void AsyncGetObjects(Array objectIds, AsyncSearchCallback callback, object context)
		{
			Hashtable state;
			AsyncCallback zOGIcallback;
			
			server.Log.Debug("AsyncGetObjects");
			state = new Hashtable();
			state.Add("callback", callback);
			state.Add("context", context);
			state.Add("method", "GetObjects");
		  zOGIcallback = new AsyncCallback(MultiResultCallback);
		  proxy.BeginGetObjects(objectIds, 65535, zOGIcallback, state);
		}		
		
		protected void SingleResultCallback(IAsyncResult asyncResult)
		{
			//XmlRpcAsyncResult clientResult;
			XmlRpcStruct			hash;
			Hashtable					state;
			AsyncGetCallback	callback;
			
			state = (Hashtable)asyncResult.AsyncState;
			callback = (AsyncGetCallback)state["callback"];
			try
			{
				hash = proxy.EndGetObject(asyncResult);
				callback(FactoryFromStruct(hash), state["context"], null);
			} catch (Exception ex)
				{
					Console.WriteLine(ex.Message);
					callback(null, state["context"], ex);
				}
		} // End SingleResultCallback
		
		protected void MultiResultCallback(IAsyncResult asyncResult)
		{
			//XmlRpcAsyncResult		clientResult;
			object							result;
			Hashtable						state;
			AsyncSearchCallback	callback;
			
			state = (Hashtable)asyncResult.AsyncState;
			callback = (AsyncSearchCallback)state["callback"];
			try
			{
				server.Log.Debug("MultiResultCallback");
				result = null;
				Console.WriteLine("MultiResultCallback:method={0}", (string)state["method"]);
				switch((string)state["method"])
				{
					case "GetFavoriteEnterprises":
						result = proxy.EndGetFavoritesByType(asyncResult);
						break;
					case "GetFavoriteContacts":
						result = proxy.EndGetFavoritesByType(asyncResult);
						break;
					case "GetFavoriteProjects":
						result = proxy.EndGetFavoritesByType(asyncResult);
						break;
					case "GetObjects":
						result = proxy.EndGetObjects(asyncResult);
						break;
					case "GetTaskList:todo":
					case "GetTaskList:archived":
					case "GetTaskList:delegated":
					  Console.WriteLine("zOGI.MultiResultCallback processing {0}", 
					  	(string)state["method"]);
						result = proxy.EndGetTasksByList(asyncResult);
						break;
					default:
					  Console.WriteLine("Fell through to default handler in MultiResultCallBack");
					  result = new XmlRpcStruct[0];
						break;
				}
				if(result is Array)
						callback(FactoryFromStructs((Array)result), state["context"], null);
					else
					{
						Console.WriteLine("zOGI.MultiResultCallback result is not an array");
						callback(new ArrayList(), 
										state["context"], 
										new Exception("Type other than Array recieved.")
									);
					}
			} catch (Exception ex)
				{
					Console.WriteLine(ex.Message);
					Console.WriteLine(ex.StackTrace);
					callback(new ArrayList(), state["context"], ex);;
				}
		} /// End MultiResultCallback	
		
		public string GetName(string objectId)
		{
			IMOGIObject	obj;
			
			if (cache.Contains(objectId))
			{
				Console.WriteLine("Name of object retrieved from cache!");
				return cache.Get(objectId).Name;
			}
			// We just checked the cache, so no reason to do that again
 			obj = GetObjectById(objectId, false);
 			// If the object can't be retrieved then return unknown
 			if (obj == null)
 				return "Document/Unknown/" + objectId;
 			return obj.Name;
		}		
	}
}
